#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
#include <unordered_map>
#include <iterator>

// 分割字符串
std::vector<std::string> splitString(const std::string& str, char delimiter) {
    std::vector<std::string> tokens;
    std::istringstream tokenStream(str);
    std::string token;

    // 处理开始处的分隔符
    if (str[0] == delimiter) {
        tokens.push_back("");
    }

    // 处理字符串中的分隔符
    while (std::getline(tokenStream, token, delimiter)) {
        tokens.push_back(token);
    }

    // 处理结尾处的分隔符
    if (!str.empty() && str[str.size() - 1] == delimiter) {
        tokens.push_back("");
    }

    return tokens;
}

// Proxy 代理

class Proxy {
    private:
    std::string result;
    std::string isProxy;
    std::string proxyType;
    std::string proxyTime;

    void parseProxy() {
        auto infos = splitString(result, '|');
        if (infos.size() != 3) {
            std::cerr << "查询结果格式错误或离线库非代理v2库" << std::endl;
        }
        if (infos[0] == "否") {
            proxyType = "不是代理";
        }else {
            isProxy = infos[0];
            proxyType = infos[1];
            proxyTime = infos[2];
        }
    }

    public:
    Proxy(const std::string& result) : result(result) {
        parseProxy();
    }
    std::string getProxyType() const { return proxyType; }
    std::string getIsProxy() const { return isProxy; }
    std::string getProxyTime() const { return proxyTime; }
};


// Scenes 场景
class Scenes {
private:
    std::string result;
    std::string scenes;

    void parseScene() {
        auto infos = splitString(result, '|');
        if (infos.size() != 1) {
            throw std::invalid_argument("");
            return;
        }
        static const std::unordered_map<std::string, std::string> sceneMap = {
            {"EDU", "教育机构"}, {"GTW", "企业专线"}, {"GOV", "政府机构"},
            {"NET", "基础设施"}, {"ORG", "组织机构"}, {"DYN", "家庭宽带"},
            {"IDC", "数据中心"}, {"MOB", "移动网络"}, {"CDN", "内容分发"},
            {"DNS", "域名解析"},{"BOGON", "保留"}
        };
        // 如果找到，则返回对应值，否则返回默认值
        try {
            // 使用 at 查找键，键不存在时会抛出异常
            scenes = sceneMap.at(infos[0]);
        } catch (const std::out_of_range&) {
            // 如果键不存在，捕获异常并返回默认值
            scenes = "";
        }
    }

public:
    Scenes(const std::string& result) : result(result) {
        parseScene();
    }
    std::string getScenes() const { return scenes; }
};

// Location 归属地
class Location {
private:
    std::string continent;       // 大洲
    std::string country;         // 国家/地区
    std::string province;        // 省份
    std::string city;            // 城市
    std::string district;        // 区县
    std::string isp;             // 运营商
    std::string areaCode;        // 区域代码
    std::string countryEnglish;  // 国家/地区英文
    std::string countryCode;     // 国家/地区英文简写
    std::string longitude;       // 经度
    std::string latitude;        // 纬度
    std::string elevation;       // 海拔
    std::string zipCode;         // 邮编
    std::string cityCode;        // 城市代码
    std::string weatherStation;  // 气象站
    std::string asn;             // ASN码
    std::string preserve;        // 预留
    std::string street;          // 街道
    std::string timeZone;        // 时区

    std::string result;

    void parseLocation() {
        auto infos = splitString(result, '|');
        if (infos.size() != 19) {
            throw std::invalid_argument("查询结果格式错误或离线库非归属地库");
        }
        continent = infos[0];
        country = infos[1];
        province = infos[2];
        city = infos[3];
        district = infos[4];
        isp = infos[5];
        areaCode = infos[6];
        countryEnglish = infos[7];
        countryCode = infos[8];
        longitude = infos[9];
        latitude = infos[10];
        elevation = infos[11];
        zipCode = infos[12];
        cityCode = infos[13];
        weatherStation = infos[14];
        asn = infos[15];
        preserve = infos[16];
        street = infos[17];
        timeZone = infos[18];
    }

public:
    Location(const std::string& result) : result(result) {
        parseLocation();
    }

    std::string getContinent() const { return continent; }
    std::string getCountry() const { return country; }
    std::string getProvince() const { return province; }
    std::string getCity() const { return city; }
    std::string getDistrict() const { return district; }
    std::string getIsp() const { return isp; }
    std::string getAreaCode() const { return areaCode; }
    std::string getCountryEnglish() const { return countryEnglish; }
    std::string getCountryCode() const { return countryCode; }
    std::string getLongitude() const { return longitude; }
    std::string getLatitude() const { return latitude; }
    std::string getElevation() const { return elevation; }
    std::string getZipCode() const { return zipCode; }
    std::string getCityCode() const { return cityCode; }
    std::string getWeatherStation() const { return weatherStation; }
    std::string getAsn() const { return asn; }
    std::string getPreserve() const { return preserve; }
    std::string getStreet() const { return street; }
    std::string getTimeZone() const { return timeZone; }

    std::string toString() const {
        std::ostringstream oss;
        oss << "Location{"
            << "continent='" << continent << '\''
            << ", country='" << country << '\''
            << ", province='" << province << '\''
            << ", city='" << city << '\''
            << ", district='" << district << '\''
            << ", isp='" << isp << '\''
            << ", areaCode='" << areaCode << '\''
            << ", countryEnglish='" << countryEnglish << '\''
            << ", countryCode='" << countryCode << '\''
            << ", longitude='" << longitude << '\''
            << ", latitude='" << latitude << '\''
            << ", elevation='" << elevation << '\''
            << ", zipCode='" << zipCode << '\''
            << ", cityCode='" << cityCode << '\''
            << ", weatherStation='" << weatherStation << '\''
            << ", asn='" << asn << '\''
            << ", preserve='" << preserve << '\''
            << ", street='" << street << '\''
            << ", timeZone='" << timeZone << '\''
            << '}';
        return oss.str();
    }
};
